/**
 * 资源加载和卸载
 * 1. 加载资源
 * 2. 卸载资源
 */
import {  __private, assert, Asset,  assetManager,  AssetManager,  Constructor, js, resources, warn } from 'cc';

export type ProgressCallback = __private._cocos_asset_asset_manager_deprecated__LoadProgressCallback;
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
export type CompleteCallback<T = any> = any;
export type AssetType<T = Asset> = Constructor<T>;
export type Paths = string | string[];


interface ILoadResArgs<T extends Asset> {
    bundle?: string;
    dir?: string;
    paths: string | string[];
    type: AssetType<T> | null;
    onProgress: ProgressCallback | null;
    onComplete: CompleteCallback<T[]> | null
}

/**
 * 游戏资源管理
 * 1.加载默认resources资源
 * 2.加载默认bundle远程资源
 * 3.主动传递bundle名时，优先加载传递bundle名资源包中的资源
 */

export default class ResLoader  {

    /** 全局默认加载的资源包名 */
    defaultBundleName: string = "resources";

    /** 是否使用远程 CDN 资源 */
    cdn: boolean = false;

    /** 下载时的最大并发数 - 项目设置 -> 项目数据 -> 资源下载并发数，设置默认值；初始值为15 */
    get maxConcurrency() {
        return assetManager.downloader.maxConcurrency;
    }
    set maxConcurrency(value) {
        assetManager.downloader.maxConcurrency = value;
    }

    /** 下载时每帧可以启动的最大请求数 - 默认值为15 */
    get maxRequestsPerFrame() {
        return assetManager.downloader.maxRequestsPerFrame;
    }
    set maxRequestsPerFrame(value) {
        assetManager.downloader.maxRequestsPerFrame = value;
    }

    /** 失败重试次数 - 默认值为0 */
    get maxRetryCount() {
        return assetManager.downloader.maxRetryCount;
    }
    set maxRetryCount(value) {
        assetManager.downloader.maxRetryCount = value;
    }

    /** 重试的间隔时间，单位为毫秒 - 默认值为2000毫秒 */
    get retryInterval() {
        return assetManager.downloader.retryInterval;
    }
    set retryInterval(value) {
        assetManager.downloader.retryInterval = value;
    }

    /** 资源包配置 */
    private bundles: Map<string, string> = new Map<string, string>();
    //#endregion

    // init(config: any) {
    //     this.cdn = config.enable;
    //     for (let bundleName in config.packages) {
    //         this.bundles.set(bundleName, config.packages[bundleName]);
    //     }
    // }

    //#region 加载远程资源

    /**
     * 加载一个资源
     * @param bundleName    远程包名
     * @param paths         资源路径
     * @param type          资源类型
     * @param onProgress    加载进度回调
     * @param onComplete    加载完成回调
     * @example todo
     */
    load<T extends Asset>(
        bundleName: string,
        paths?: string | string[] | AssetType<T> | ProgressCallback | CompleteCallback | null,
        type?: AssetType<T> | ProgressCallback | CompleteCallback | null,
        onProgress?: ProgressCallback | CompleteCallback | null,
        onComplete?: CompleteCallback | null,
    ) {
        let args: ILoadResArgs<T> | null = null;
        if (typeof paths === "string" || paths instanceof Array) {
            args = this.parseLoadResArgs(paths, type, onProgress, onComplete);
            args.bundle = bundleName;
        }
        else {
            args = this.parseLoadResArgs(bundleName, paths, type, onProgress);
            args.bundle = this.defaultBundleName;
        }
        this.loadByArgs(args);
    }

    /**
     * 异步加载一个资源
     * @param bundleName    远程包名
     * @param paths         资源路径
     * @param type          资源类型
     */
    loadAsync<T extends Asset>(bundleName: string, paths: Paths, type: AssetType<T>): Promise<T>;
    loadAsync<T extends Asset>(bundleName: string, paths: Paths): Promise<T>;
    loadAsync<T extends Asset>(paths: Paths, type: AssetType<T>): Promise<T>;
    loadAsync<T extends Asset>(paths: Paths): Promise<T>;
    loadAsync<T extends Asset>(bundleName: string,
        paths?: Paths | AssetType<T> | ProgressCallback | CompleteCallback,
        type?: AssetType<T> | ProgressCallback | CompleteCallback): Promise<T> {
        return new Promise((resolve) => {
            this.load(bundleName, paths, type, (err: Error | null, asset: T) => {
                if (err) {
                    warn(err.message);
                }
                resolve(asset);
            });
        });
    }

    /** 打印缓存中所有资源信息 */
    dump() {
        assetManager.assets.forEach((value: Asset, key: string) => {
            console.log(assetManager.assets.get(key));
        })
        console.log(`当前资源总数:${assetManager.assets.count}`);
    }

    private parseLoadResArgs<T extends Asset>(
        paths: string | string[],
        type?: AssetType<T> | ProgressCallback | CompleteCallback | null,
        onProgress?: AssetType<T> | ProgressCallback | CompleteCallback | null,
        onComplete?: ProgressCallback | CompleteCallback | null
    ){
        const pathsOut: string | string[] = paths;
        let typeOut: AssetType<T> | ProgressCallback | CompleteCallback | null = type;
        let onProgressOut: ProgressCallback | CompleteCallback | null = onProgress;
        let onCompleteOut: CompleteCallback | null = onComplete;

        if (onComplete === undefined) {
            const isValidType = js.isChildClassOf(type as AssetType, Asset);
            if (onProgress) {
                onCompleteOut = onProgress as CompleteCallback;
                if (isValidType) {
                    onProgressOut = null;
                }
            }
            else if (onProgress === undefined && !isValidType) {
                onCompleteOut = type as CompleteCallback;
                onProgressOut = null;
                typeOut = null;
            }
            if (onProgress !== undefined && !isValidType) {
                onProgressOut = type as ProgressCallback;
                typeOut = null;
            }
        }
        return {
            paths: pathsOut,
            type: typeOut,
            onProgress: onProgressOut,
            onComplete: onCompleteOut
        }
    }

    private loadByByBundleAndArgs<T extends Asset>(bundle: AssetManager.Bundle, args: ILoadResArgs<T>): void {
        if (args?.dir) {
            bundle.loadDir(args.paths as string, args.type, args.onProgress, args.onComplete);
        }
        else {
            if (typeof args?.paths === 'string') {
                bundle.load(args.paths, args.type, args.onProgress, args.onComplete);
            } else {
                bundle.load(args.paths, args.type, args.onProgress, args.onComplete);

            }
        }
    }

    private loadByArgs<T extends Asset>(args: ILoadResArgs<T>): void {
        if (args?.bundle) {
            if (assetManager.bundles.has(args.bundle)) {
                const bundle = assetManager.bundles.get(args.bundle);
                assert(!!bundle);
                this.loadByByBundleAndArgs(bundle, args);

            }
            else {
                assetManager.loadBundle(args.bundle, (err: Error | null, bundle: AssetManager.Bundle) => {
                    if (!err) {
                        this.loadByByBundleAndArgs(bundle, args);
                    }
                })
            }
        }
        else {
            this.loadByByBundleAndArgs(resources, args);
        }
    }
}




export const resolader = new ResLoader();